/*
 * Copyright (c) 2023 DuckDuckGo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.duckduckgo.autofill.impl.ui.credential.passwordgeneration

import com.duckduckgo.autofill.api.ExistingCredentialMatchDetector.ContainsCredentialsResult
import com.duckduckgo.autofill.api.ExistingCredentialMatchDetector.ContainsCredentialsResult.UsernameMatchDifferentPassword
import com.duckduckgo.autofill.api.ExistingCredentialMatchDetector.ContainsCredentialsResult.UsernameMatchMissingPassword
import com.duckduckgo.autofill.api.domain.app.LoginCredentials
import com.duckduckgo.autofill.impl.ui.credential.passwordgeneration.Actions.DeleteAutoLogin
import com.duckduckgo.autofill.impl.ui.credential.passwordgeneration.Actions.DiscardAutoLoginId
import com.duckduckgo.autofill.impl.ui.credential.passwordgeneration.Actions.PromptToSave
import com.duckduckgo.autofill.impl.ui.credential.passwordgeneration.Actions.UpdateMatchingUsernames
import com.duckduckgo.autofill.impl.ui.credential.passwordgeneration.Actions.UpdateSavedAutoLogin
import com.duckduckgo.di.scopes.AppScope
import com.squareup.anvil.annotations.ContributesBinding
import logcat.LogPriority.VERBOSE
import logcat.logcat
import javax.inject.Inject

interface AutogeneratedPasswordEventResolver {
    suspend fun decideActions(
        autoSavedLogin: LoginCredentials?,
        autogenerated: Boolean,
        matchType: ContainsCredentialsResult,
        backFilled: Boolean,
        username: String?,
    ): List<Actions>
}

sealed interface Actions {
    data object PromptToSave : Actions
    data class UpdateSavedAutoLogin(val autologinId: Long) : Actions
    data class DeleteAutoLogin(val autologinId: Long) : Actions
    data object DiscardAutoLoginId : Actions
    data class UpdateMatchingUsernames(val username: String) : Actions
}

@ContributesBinding(AppScope::class)
class PasswordEventResolver @Inject constructor() : AutogeneratedPasswordEventResolver {

    override suspend fun decideActions(
        autoSavedLogin: LoginCredentials?,
        autogenerated: Boolean,
        matchType: ContainsCredentialsResult,
        backFilled: Boolean,
        username: String?,
    ): List<Actions> {
        val outcomes = mutableListOf<Actions>()
        val autoSavedLoginId = autoSavedLogin?.id

        logcat(VERBOSE) {
            """Deciding autofill actions.
                autoSavedLoginId: $autoSavedLoginId
                autogenerated: $autogenerated
                matchType: $matchType
                backFilled: $backFilled
                username: $username
            """.trimIndent()
        }

        if (autoSavedLoginId == null) {
            if (username != null && backFilled && matchType is UsernameMatchMissingPassword) {
                handleBackFilledUsernameScenario(username, outcomes)
            } else {
                outcomes.add(PromptToSave)
            }
        } else if (autogenerated) {
            handleAutogeneratedScenario(matchType, outcomes, autoSavedLoginId)
        } else {
            outcomes.add(DeleteAutoLogin(autoSavedLoginId))
            outcomes.add(DiscardAutoLoginId)
            outcomes.add(PromptToSave)
        }

        return outcomes
    }

    private fun handleBackFilledUsernameScenario(username: String, outcomes: MutableList<Actions>) {
        outcomes.add(UpdateMatchingUsernames(username))
    }

    /**
     * Autogenerated scenario means that we have already saved a login without prompting, so requires special handling
     * What we do in this scenario depends on the match type. Sometimes we'd silently update it, sometimes we'll prompt the user if they want to.
     */
    private fun handleAutogeneratedScenario(
        matchType: ContainsCredentialsResult,
        outcomes: MutableList<Actions>,
        autoSavedLoginId: Long,
    ) {
        when (matchType) {
            UsernameMatchDifferentPassword, UsernameMatchMissingPassword -> {
                outcomes.add(DeleteAutoLogin(autoSavedLoginId))
                outcomes.add(DiscardAutoLoginId)
                outcomes.add(PromptToSave)
            }

            else -> {
                outcomes.add(UpdateSavedAutoLogin(autoSavedLoginId))
            }
        }
    }
}
